package edu.kit.aifb.ldsbench.cli2;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.sail.nativerdf.NativeStore;

import edu.kit.aifb.ldsbench.cli.util2.ModStatsXMLReader;
import edu.kit.aifb.ldsbench.generator2.Generator;
import edu.kit.aifb.ldsbench.out2.FileOutput;
import edu.kit.aifb.ldsbench.out2.IGeneratorOutput;
import edu.kit.aifb.ldstream.env.Environment;
import edu.kit.aifb.ldstream.learner.tasks.mod.ModStatistics;
import edu.kit.aifb.ldstream.repository.nquad.NQuadHandler;
import edu.kit.aifb.ldstream.repository.nquad.NQuadsParser;

import joptsimple.OptionParser;
import joptsimple.OptionSet;

public class CLI {

	private static final String logPath = "./gen_log.log";

	private static final String statsOption = "s";
	private static final String schemaOption = "sc";
	private static final String propOption = "p";
	private static final String valueOption = "v";
	private static final String repoOption = "r";
	private static final String outOption = "o";
	private static final String eventsOption = "e";
	private static final String mappingOption = "m";
	private static final String instanceNameOption = "i";

	public static Repository REPO ;
	public static ModStatistics STATS;


	public final static Logger log; 

	static{
		// setting up the logger 
		log = Logger.getLogger("LDS_Bench_Log");
		log.setLevel(Level.INFO);
		File f = new File(logPath);
		if(f.exists()){
			f.delete();
		}
		try {
			f.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		SimpleFormatter formatter = new SimpleFormatter();
		FileHandler fh = null;
		try {
			fh = new FileHandler(logPath, true);
			fh.setFormatter(formatter);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		log.addHandler(fh);

	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		OptionParser op = new OptionParser();
		op.accepts(schemaOption).withRequiredArg().ofType(String.class);
		op.accepts(propOption).withRequiredArg().ofType(String.class);
		op.accepts(valueOption).withRequiredArg().ofType(String.class);
		op.accepts(repoOption).withRequiredArg().ofType(String.class);
		op.accepts(outOption).withRequiredArg().ofType(String.class);
		op.accepts(statsOption).withRequiredArg().ofType(String.class);
		op.accepts(eventsOption).withRequiredArg().ofType(Integer.class);
		op.accepts(instanceNameOption).withRequiredArg().ofType(String.class);
		op.accepts(mappingOption).withRequiredArg().ofType(String.class);
		OptionSet os = op.parse(args);

		if(init(os)){
			int numEvents = (Integer) os.valueOf(eventsOption);

			//starting the generation process 
			if(os.has(CLI.outOption)){
				IGeneratorOutput out = new FileOutput((String) os.valueOf(outOption));
				Generator generator = new Generator(numEvents, out);
				generator.run();
			}
		}else{
			System.out.println("Could not initialise.");
		}
		shutdown();
	}

	private static void shutdown(){

		try {
			REPO.shutDown();
		} catch (RepositoryException e) {}
		if(log.isLoggable(Level.INFO)) log.info("Repository shut down");
	}

	private static boolean init(OptionSet os){
		boolean success = false;
		ModStatsXMLReader msReader = new ModStatsXMLReader((String) os.valueOf(statsOption));
		STATS = msReader.read();

		Repository repo = initRepo(os);
		if(repo != null){
			if(log.isLoggable(Level.INFO)) log.info("Repository initialised");
			REPO = repo;
			success = true;
		}
		return success;

	}

	private  static Repository initRepo(OptionSet os){
		boolean success = false;
		Repository repo = null;
		if(os.has(repoOption) && os.has(valueOption) && os.has(schemaOption) && os.has(propOption)){

			File repofile = new File((String)  os.valueOf(repoOption));

			if(repofile.isDirectory()){
				String[] files = repofile.list();
				File deleteFile;
				for(String fileName : files){
					deleteFile = new File(repofile.getAbsoluteFile() + fileName);
					deleteFile.delete();
				}
			}else if(repofile.exists()){
				try {
					repofile.delete();
					repofile.createNewFile();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			repo = new SailRepository(new NativeStore(repofile));
			try {
				repo.initialize();
				success = true;
			} catch (RepositoryException e) {
				e.printStackTrace();
			}

			//add the schema
			if(success){
				success = readSchema(os, repo);
			}
			//add the value results
			if(success){
				success = readValueResult(os, repo);
			}
			//add the property results
			if(success){
				success = readPropResult(os, repo);
			}
			if(success){
				success = readInstanceNames(os, repo);
			}
			if(success){
				success = readMappings(os, repo);
			}
		}
		if(success){
			return repo;
		}
		return null;
	}
	private static boolean  readSchema(OptionSet os, Repository repo){
		boolean success = false;
		if(log.isLoggable(Level.FINEST)) log.entering(CLI.class.toString(), "readSchema");

		try {
			RepositoryConnection con = repo.getConnection();
			con.add(new File((String) os.valueOf(schemaOption)), null, RDFFormat.forFileName((String) os.valueOf(schemaOption), RDFFormat.RDFXML) /*RDFFormat.RDFXML*/, Environment.SCHEMA_URI);
			con.close();
			success = true;
		} catch (RepositoryException e) {
			e.printStackTrace();
		} catch (RDFParseException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if(log.isLoggable(Level.FINEST)) log.exiting(CLI.class.toString(), "readSchema");

		return success;
	}


	private static boolean readInstanceNames(OptionSet os, Repository repo){
		boolean success = true;
		if(os.has(instanceNameOption)){
			try{
				NQuadsParser parser = new NQuadsParser();
				parser.setRDFHandler(new NQuadHandler(repo));
				InputStream is = new FileInputStream((String) os.valueOf(instanceNameOption));
				parser.parse(is, "");
				is.close();
			} catch (FileNotFoundException e) {
				success = false;
				e.printStackTrace();
			} catch (RDFParseException e) {
				success = false;
				e.printStackTrace();
			} catch (RDFHandlerException e) {
				success = false;
				e.printStackTrace();
			} catch (IOException e) {
				success = false;
				e.printStackTrace();
			}
		}
		return success;
	}
	
	
	private static boolean readMappings(OptionSet os, Repository repo){
		boolean success = true;
		if(os.has(mappingOption)){
			try{
				NQuadsParser parser = new NQuadsParser();
				parser.setRDFHandler(new NQuadHandler(repo));
				InputStream is = new FileInputStream((String) os.valueOf(mappingOption));
				parser.parse(is, "");
				is.close();
			} catch (FileNotFoundException e) {
				success = false;
				e.printStackTrace();
			} catch (RDFParseException e) {
				success = false;
				e.printStackTrace();
			} catch (RDFHandlerException e) {
				success = false;
				e.printStackTrace();
			} catch (IOException e) {
				success = false;
				e.printStackTrace();
			}
		}

		return success;
	}


	private static boolean readPropResult(OptionSet os, Repository repo){
		boolean success = false;
		if(log.isLoggable(Level.FINEST)) log.entering(CLI.class.toString(), "readPropResult");

		try {
			NQuadsParser parser = new NQuadsParser();
			parser.setRDFHandler(new NQuadHandler(repo));
			InputStream is = new FileInputStream((String) os.valueOf(propOption));
			parser.parse(is, "");
			is.close();
			success = true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (RDFParseException e) {
			e.printStackTrace();
		} catch (RDFHandlerException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if(log.isLoggable(Level.FINEST)) log.exiting(CLI.class.toString(), "readPropResult");

		return success;
	}


	private static boolean readValueResult(OptionSet os, Repository repo){
		boolean success = false;
		if(log.isLoggable(Level.FINEST)) log.entering(CLI.class.toString(), "readValueResult");

		try {
			NQuadsParser parser = new NQuadsParser();
			parser.setRDFHandler(new NQuadHandler(repo));
			InputStream is = new FileInputStream((String) os.valueOf(valueOption));
			parser.parse(is, "");
			is.close();
			success = true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (RDFParseException e) {
			e.printStackTrace();
		} catch (RDFHandlerException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if(log.isLoggable(Level.FINEST)) log.exiting(CLI.class.toString(), "readValueResult");

		return success;
	}

}
